package net.goutalk.glcs.module.organization.controller;

import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.yulichang.toolkit.MPJWrappers;
import net.goutalk.glcs.common.constant.GlobalConstant;
import net.goutalk.glcs.common.enums.EnabledMark;
import net.goutalk.glcs.common.exception.MyException;
import net.goutalk.glcs.common.model.result.R;
import net.goutalk.glcs.common.page.ConventPage;
import net.goutalk.glcs.common.page.PageOutput;
import net.goutalk.glcs.common.utils.RedisUtil;
import net.goutalk.glcs.common.utils.VoToColumnUtil;
import net.goutalk.glcs.config.CommonPropertiesConfig;
import net.goutalk.glcs.module.organization.dto.*;
import net.goutalk.glcs.module.organization.entity.*;
import net.goutalk.glcs.module.organization.service.*;
import net.goutalk.glcs.module.organization.dto.*;
import net.goutalk.glcs.module.organization.entity.*;
import net.goutalk.glcs.module.organization.utils.OrganizationUtil;
import net.goutalk.glcs.module.organization.vo.*;
import net.goutalk.glcs.module.organization.vo.*;
import net.goutalk.glcs.module.oss.factory.OssFactory;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import net.goutalk.glcs.module.organization.service.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户 前端控制器
 * </p>
 *
 * @author tanyujie
 * @since 2022-03-02
 */
@RestController
@RequestMapping(GlobalConstant.ORGANIZATION_MODULE_PREFIX + "/user")
@Api(value = GlobalConstant.ORGANIZATION_MODULE_PREFIX + "/user", tags = "用户")
@AllArgsConstructor
public class UserController {

    private final IUserService userService;

    private final IDepartmentService departmentService;

    private final IPostService postService;

    private final IRoleService roleService;

    private final IUserRoleRelationService userRoleRelationService;

    private final IUserDeptRelationService userDeptRelationService;

    private final IUserPostRelationService userPostRelationService;

    private final CommonPropertiesConfig propertiesConfig;

    private final RedisUtil redisUtil;


    @GetMapping(value = "/list")
    @ApiOperation(value = "用户列表（不分页）")
    public R list(String keyword) {

        List<User> list = userService.list(Wrappers.lambdaQuery(User.class)
                .like(StrUtil.isNotBlank(keyword), User::getUserName, keyword)
                .like(StrUtil.isNotBlank(keyword), User::getCode, keyword)
                .like(StrUtil.isNotBlank(keyword), User::getName, keyword)
                .like(StrUtil.isNotBlank(keyword), User::getMobile, keyword)
                .select(User.class, x -> VoToColumnUtil.fieldsToColumns(UserListVo.class).contains(x.getProperty())));
        List<UserListVo> userListVos = BeanUtil.copyToList(list, UserListVo.class);
        return R.ok(userListVos);
    }

    @GetMapping(value = "/page")
    @ApiOperation(value = "用户列表（分页）")
    public R page(UserPageDto dto) {
        List<Long> deptIds = new ArrayList<>();
        if (ObjectUtil.isNotNull(dto.getDepartmentId())){
            List<Department> list = redisUtil.get(GlobalConstant.DEP_CACHE_KEY,new TypeReference<List<Department>>() {
            });
            //当前部门的层级
            String hierarchy = list.stream().filter(x -> x.getId().equals(dto.getDepartmentId())).findFirst().orElse(new Department()).getHierarchy();
            if (StrUtil.isNotBlank(hierarchy)){
                //层级里面包含当前部门层级的就是它的子集，如1-1，下面包含了1-1、1-1-2这种
                deptIds = list.stream().filter(x -> StrUtil.isNotBlank(x.getHierarchy())&&x.getHierarchy().contains(hierarchy)).map(Department::getId).collect(Collectors.toList());
            }else {
                //如果不存在层级就查询自己的数据
                deptIds.add(dto.getDepartmentId());
            }
        }

        //因为多表关联 会有多个表都使用了id字段，  所以必须专门指定主表的Id
        if (ObjectUtil.isNotNull(dto.getDepartmentId())){//不为空联合查询
            IPage<UserPageVo> page = userService.selectJoinListPage(ConventPage.getPage(dto), UserPageVo.class,
                    MPJWrappers.<User>lambdaJoin()
                            .distinct()
                            .like(StrUtil.isNotBlank(dto.getKeyword()), User::getName, dto.getKeyword())
                            .or(StrUtil.isNotBlank(dto.getKeyword()), x -> x.like(StrUtil.isNotBlank(dto.getKeyword()), User::getCode, dto.getKeyword()))
                            .in(ObjectUtil.isNotNull(dto.getDepartmentId()), UserDeptRelation::getDeptId, deptIds)
                            .like(StrUtil.isNotBlank(dto.getUserName()), User::getUserName, dto.getUserName())
                            .like(StrUtil.isNotBlank(dto.getCode()), User::getCode, dto.getCode())
                            .like(StrUtil.isNotBlank(dto.getName()), User::getName, dto.getName())
                            .like(StrUtil.isNotBlank(dto.getMobile()), User::getMobile, dto.getMobile())
                            .orderByDesc(User::getCreateDate)
                            .select(User::getId)
                            .select(User.class, x -> VoToColumnUtil.fieldsToColumns(UserPageVo.class).contains(x.getProperty()))
                            .leftJoin(UserDeptRelation.class, UserDeptRelation::getUserId, User::getId));

            PageOutput<UserPageVo> pageOutput = ConventPage.getPageOutput(page);

            return R.ok(pageOutput);
        }else {
            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.like(StrUtil.isNotBlank(dto.getKeyword()), User::getName, dto.getKeyword())
                    .or(StrUtil.isNotBlank(dto.getKeyword()), x -> x.like(StrUtil.isNotBlank(dto.getKeyword()), User::getCode, dto.getKeyword()))
                    .like(StrUtil.isNotBlank(dto.getUserName()), User::getUserName, dto.getUserName())
                    .like(StrUtil.isNotBlank(dto.getCode()), User::getCode, dto.getCode())
                    .like(StrUtil.isNotBlank(dto.getName()), User::getName, dto.getName())
                    .like(StrUtil.isNotBlank(dto.getMobile()), User::getMobile, dto.getMobile())
                    .orderByDesc(User::getCreateDate)
                    .select(User.class, x -> VoToColumnUtil.fieldsToColumns(UserPageVo.class).contains(x.getProperty()));

            IPage<User> page = userService.page(ConventPage.getPage(dto),queryWrapper);
            PageOutput<UserPageVo> pageOutput = ConventPage.getPageOutput(page, UserPageVo.class);
            return R.ok(pageOutput);
        }
    }

    @GetMapping(value = "/info")
    @ApiOperation(value = "根据id查询用户信息")
    public R info(@RequestParam Long id) {
        User user = userService.getById(id);
        if (user == null) {
            R.error("找不到此用户！");
        }
        UserVo userVo = BeanUtil.toBean(user, UserVo.class);
        List<Long> deptIds = userDeptRelationService.list(Wrappers.lambdaQuery(UserDeptRelation.class)
                        .eq(UserDeptRelation::getUserId, user.getId().toString()))
                .stream().map(UserDeptRelation::getDeptId).collect(Collectors.toList());
        String allDeptIdStr = StrUtil.join(StringPool.COMMA, deptIds);
        userVo.setDepartmentIds(allDeptIdStr);
        return R.ok(userVo);
    }


    @PostMapping
    @ApiOperation(value = "新增用户")

    public R add(@Valid @RequestBody AddUserDto dto) {
        if (!OrganizationUtil.validatePassword(dto.getPassword())) {
            return R.error("密码必须包含大写字母、小写字母、数字和特殊字符，长度8~16位");
        }
        return R.ok(userService.add(dto));
    }

    @PutMapping
    @ApiOperation(value = "修改用户 不能修改用户名")
    public R update(@Valid @RequestBody UpdateUserDto dto) {
        return R.ok(userService.update(dto));
    }


    @GetMapping(value = "/current/info")
    @ApiOperation(value = "当前登录用户信息")
    public R info() {

        SaSession tokenSession = StpUtil.getTokenSession();
        User user = tokenSession.get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());

        List<Long> roleIds = userRoleRelationService.list(Wrappers.lambdaQuery(UserRoleRelation.class)
                        .eq(UserRoleRelation::getUserId, user.getId().toString()))
                .stream().map(UserRoleRelation::getRoleId).collect(Collectors.toList());

        List<Long> deptIds = userDeptRelationService.list(Wrappers.lambdaQuery(UserDeptRelation.class)
                        .eq(UserDeptRelation::getUserId, user.getId().toString()))
                .stream().map(UserDeptRelation::getDeptId).collect(Collectors.toList());

        List<Long> postIds = userPostRelationService.list(Wrappers.lambdaQuery(UserPostRelation.class)
                        .eq(UserPostRelation::getUserId, user.getId().toString()))
                .stream().map(UserPostRelation::getPostId).collect(Collectors.toList());

        UserInfoVo vo = BeanUtil.toBean(user, UserInfoVo.class);
        if (roleIds.size() > 0) {

            List<Role> list = roleService.list(Wrappers.lambdaQuery(Role.class).in(Role::getId, roleIds));

            List<UserRoleVo> userRoleVoList = BeanUtil.copyToList(list, UserRoleVo.class);
            vo.setRoles(userRoleVoList);

        }


        if (deptIds.size() > 0) {
            List<Department> list = departmentService.list(Wrappers.lambdaQuery(Department.class).in(Department::getId, deptIds));

            List<UserDeptVo> userDeptVoList = BeanUtil.copyToList(list, UserDeptVo.class);
            vo.setDepartments(userDeptVoList);

        }


        if (postIds.size() > 0) {
            List<Post> list = postService.list(Wrappers.lambdaQuery(Post.class).in(Post::getId, postIds));

            List<UserPostVo> userPostVoList = BeanUtil.copyToList(list, UserPostVo.class);
            vo.setPosts(userPostVoList);

        }


        return R.ok(vo);
    }


    @PutMapping("/update/info")
    @ApiOperation(value = "登陆人修改自己得用户信息")
    public R updateInfo(@RequestBody @Valid UpdateInfoDto dto) {
        User updateUserInfo = BeanUtil.toBean(dto, User.class);
        updateUserInfo.setId(StpUtil.getLoginIdAsLong());

        CompletableFuture.runAsync(() -> {
            List<User> list = userService.list();
            redisUtil.set(GlobalConstant.USER_CACHE_KEY, list);
        });
        return R.ok(userService.updateById(updateUserInfo));
    }


    @PutMapping("/update/password")
    @ApiOperation(value = "当前登录用户修改本人密码")
    public R updatePassword(@RequestBody @Valid UpdatePasswordDto dto) {
        if (!OrganizationUtil.validatePassword(dto.getNewPassword())) {
            return R.error("密码必须包含大写字母、小写字母、数字和特殊字符，长度8~16位");
        }
        User user = userService.getById(StpUtil.getLoginIdAsLong());
        if (!StrUtil.equals(SaSecureUtil.md5BySalt(dto.getOldPassword(), GlobalConstant.SECRET_KEY), user.getPassword())) {
            return R.error("当前密码填写错误！");
        }
        if (!StrUtil.equals(dto.getNewPassword(), dto.getConfirmPassword())) {
            return R.error("2次密码输入不一致！");
        }

        User updateUser = new User();
        updateUser.setId(StpUtil.getLoginIdAsLong());
        updateUser.setPassword(SaSecureUtil.md5BySalt(dto.getNewPassword(), GlobalConstant.SECRET_KEY));

        return R.ok(userService.updateById(updateUser));
    }

    @PostMapping("/update/avatar")
    @ApiOperation(value = "当前登录用户修改头像")
    public R uploadAvatar(@RequestParam("file") MultipartFile file) throws Exception {
        if (file.isEmpty()) {
            throw new MyException("上传文件不能为空");
        }

        //上传文件
        String suffix = Objects.requireNonNull(file.getOriginalFilename()).substring(file.getOriginalFilename().lastIndexOf(StringPool.DOT));
        String url = Objects.requireNonNull(OssFactory.build()).uploadSuffix(file.getBytes(), suffix);

        User updateUser = new User();
        updateUser.setId(StpUtil.getLoginIdAsLong());
        updateUser.setAvatar(url);
        userService.updateById(updateUser);

        SaSession tokenSession = StpUtil.getTokenSession();
        User user = tokenSession.get(GlobalConstant.LOGIN_USER_INFO_KEY, new User());

        user.setAvatar(url);
        tokenSession.set(GlobalConstant.LOGIN_USER_INFO_KEY, user);


        return R.ok(url);
    }

    @DeleteMapping
    @ApiOperation(value = "删除用户(可批量)")
    public R delete(@RequestBody List<Long> ids) {
        if (ids.contains(GlobalConstant.SUPER_ADMIN_USER_ID)) {
            R.error("管理员账户不能删除！");
        }

        if (ids.contains(StpUtil.getLoginIdAsLong())) {
            R.error("当前登录账户不能删除！");
        }
        //删除时需要同时删除用户部门关联表和用户角色关联表和用户岗位关系表数据。
        return R.ok(userService.deleteBatch(ids));
    }

    @GetMapping("/info/multi")
    @ApiOperation(value = "批量获取用户信息")
    public R usersInfo(@RequestParam String ids) {
        return R.ok(userService.getUsersInfo(ids));
    }

    @GetMapping("/enabled")
    @ApiOperation(value = "启用/禁用用户")
    public R enabled(@RequestParam Long id) {
        User user = userService.getOne(Wrappers.<User>query().lambda().select(User::getEnabledMark).eq(User::getId, id), false);
        if (user != null) {
            User updateUser = new User();
            updateUser.setId(id);
            updateUser.setEnabledMark(user.getEnabledMark() == EnabledMark.ENABLED.getCode() ? EnabledMark.DISABLED.getCode() : EnabledMark.ENABLED.getCode());
            return R.ok(userService.updateById(updateUser));
        }
        CompletableFuture.runAsync(() -> {
            List<User> list = userService.list();
            redisUtil.set(GlobalConstant.USER_CACHE_KEY, list);
        });
        return R.error("该用户不存在！");
    }

    @PutMapping("/reset-password")
    @ApiOperation(value = "重置密码")
    public R resetPassword(@RequestBody ResetPasswordDto dto) {
        User user = new User();
        user.setId(dto.getId());
//        user.setPassword(DigestUtil.md5Hex(propertiesConfig.getDefaultPassword()));
        user.setPassword(SaSecureUtil.md5BySalt(propertiesConfig.getDefaultPassword(), GlobalConstant.SECRET_KEY));
        CompletableFuture.runAsync(() -> {
            List<User> list = userService.list();
            redisUtil.set(GlobalConstant.USER_CACHE_KEY, list);
        });
        return R.ok(userService.updateById(user));
    }

}
